/*
---------------------------------------
By: 	Wei YE ( ye0003ei@e.ntu.edu.sg )

Date:	20/Aug/2012

-----------------------------------------
*/
#pragma once

#include "cv.h"
#include "SLIC.h"
#include "utilities.h"

using namespace cv;
using namespace std;

#define NUM_CHANNELS 4
#define NUM_BINS 9
#define PI 3.14159265f 
#define HSI_HIST 0
#define H_HIST   1
#define S_HIST   2
#define I_HIST   3

//-------------------------------------//
//          Class -- HsiHist           //
//-------------------------------------//

class HsiHist
{
public:

	HsiHist():hbins_(8), sbins_(8), ibins_(8){};

	~HsiHist(){};

	inline void setBinNum(int h, int s, int i){hbins_ = h; sbins_ = s; ibins_ = i;};

	inline void rgb2hsi(uchar r, uchar g, uchar b, float* hsi)
	{
		float R, G, B, H, S, I, maxc, minc;
		R = float(r); G = float(g); B = float(b);
		maxc = MAX(MAX(R,G),B); minc = MIN(MIN(R,G),B);

		if(maxc == minc)
		{
			H = 0.0f; S =0.0f;
		}else
		{
			if(maxc == R) {
				H = (G-B)/(maxc - minc)/6.0f;
				H = H >= 0? H : H+1;
			}else if(maxc == G){
				H = (2.0f + (B-R)/(maxc - minc))/6.0f;
			}else{
				H = (4.0f + (R-G)/(maxc - minc))/6.0f; //range [0,1)
			}
			S = minc == 0? 0.999f :(float)(maxc - minc)/maxc; //range [0,1)				
		}
		I = (R+G+B)/(3.0f*255 + 1.0f); // range [0,1) 
		hsi[0] = H; hsi[1] = S; hsi[2] = I;
	};

	void calcHist(const Mat& img, Mat& hist, Mat& spADM, vector<int>& ptLabels, vector<int>& spSize);

private:
	int hbins_, sbins_, ibins_;
};

//-------------------------------------//
//             Class -- HoG            //
//-------------------------------------//

class HoG {
public:
	HoG();
	~HoG()
	{
		cvReleaseMat(&Gauss);
		delete ptGauss;
	}
	int binNum()
	{
		return bins;
	};

	void extractOBin(IplImage *Iorient, IplImage *Imagn, Mat& out, vector<int>& ptLabels);

private:

	void calcHoGBin(uchar* ptOrient, uchar* ptMagn, int step, double* desc);
	void binning(float v, float w, double* desc, int maxb);

	int bins;
	float binsize; 

	int g_w;
	CvMat* Gauss;

	// Gauss as vector
	float* ptGauss;
};

inline void HoG::calcHoGBin(uchar* ptOrient, uchar* ptMagn, int step, double* desc) {
	for(int i=0; i<bins;i++)
		desc[i]=0;

	uchar* ptO = &ptOrient[0];
	uchar* ptM = &ptMagn[0];
	int i=0;
	for(int y=0;y < g_w; ++y, ptO+=step, ptM+=step) {
		for(int x = 0;x < g_w; ++x, ++i) {
			binning((float)ptO[x]/binsize, (float)ptM[x] * ptGauss[i], desc, bins);
		}
	}
}

inline void HoG::binning(float v, float w, double* desc, int maxb) {
	int bin1 = int(v);
	int bin2;
	float delta = v-bin1-0.5f;
	if(delta<0) {
		bin2 = bin1 < 1 ? maxb-1 : bin1-1; 
		delta = -delta;
	} else
		bin2 = bin1 < maxb-1 ? bin1+1 : 0; 
	desc[bin1] += (1-delta)*w;
	desc[bin2] += delta*w;
}

//-------------------------------------//
//         Class -- Features           //
//-------------------------------------//

class Features{
public:
	Features()
	{
		for(int i = 0; i < NUM_CHANNELS; i++)
			vecChannels_.push_back(new Mat());
	};

	~Features()
	{
		for(uint c = 0; c < vecChannels_.size(); c++)
			delete vecChannels_.at(c);
		vecChannels_.clear();
	};

	inline Mat* Channels(int idx) const
	{
		if(idx >= (int)vecChannels_.size()){
			return NULL;
		}
		return vecChannels_.at(idx);
	};

	inline int ChannelsNum() const
	{
		return vecChannels_.size();
	};

	inline Mat* spADM()
	{
		return &spADM_;
	};

	inline int SuperpixelNum() const 
	{
		return spNum_;
	};

	inline int ChannelDimNum(int idx) const 
	{
		if(idx >= (int)vecChannels_.size()){
			return 0;
		}
		return vecChannels_.at(idx)->cols;
	};

	inline vector<int>* PtLabels() 
	{
		return &ptLabels_;
	};

	inline vector<Point>* SuperpixelCent()
	{
		return &spCent_;
	}

	inline vector<int>* SuperpixelSize()
	{
		return &spSize_;
	}

	void Record(string file);

	void Setup(int height, int width);

	void ExtractFromImage(Mat& image);

private:
	int   spNum_;   // number of superpixels
	Mat   spADM_;   // superpixel ADjacent Matrix
	vector<int>   spSize_;
	vector<Point> spCent_;
    vector<int>   ptLabels_;
	vector<Mat*>  vecChannels_;	
};






